#ifndef _m_bufH
#define _m_bufH



#include "Endian.h"

#include <algorithm>

#include <assert.h>
#include <string.h>
#include <string>
#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>

using namespace std;

namespace loong
{

/// A buffer class modeled after org.jboss.netty.buffer.ChannelBuffer
///
/// @code
/// +-------------------+------------------+------------------+
/// | prependable bytes |  readable bytes  |  writable bytes  |
/// |                   |     (CONTENT)    |                  |
/// +-------------------+------------------+------------------+
/// |                   |                  |                  |
/// 0      <=      readerIndex   <=   writerIndex    <=     size
/// @endcode
class Buffer
{
public:
    static const size_t kInitialSize = 4096;

    Buffer()
        : m_buf(nullptr),
	  m_size(kInitialSize),
          m_rIndex(0),
          m_wIndex(0)
    {
        assert(readableBytes() == 0);
        assert(writableBytes() == kInitialSize);
        assert(frontBytes() == 0);
	m_buf=static_cast<char *>(malloc(kInitialSize));
    }
    ~Buffer()
    {
		free(m_buf);
    }

    // implicit copy-ctor, move-ctor, dtor and assignment are fine
    // NOTE: implicit move-ctor is added in g++ 4.6

    void swap(Buffer& rhs)
    {
		std::swap(m_buf, rhs.m_buf);
        std::swap(m_rIndex, rhs.m_rIndex);
        std::swap(m_wIndex, rhs.m_wIndex);
    }

    size_t readableBytes() const
    {
        return m_wIndex - m_rIndex;
    }

    size_t writableBytes() const
    {
        return m_size - m_wIndex;
    }

	//前面可以写的字节数
    size_t frontBytes() const
    {
        return m_rIndex;
    }

    const char* peek() const
    {
        return m_buf + m_rIndex;
    }

    const char* findCRLF() const
    {
        // FIXME: replace with memmem()?
        const char* crlf = std::search(peek(), beginWrite(), kCRLF, kCRLF+2);
        return crlf == beginWrite() ? NULL : crlf;
    }

    const char* findCRLF(const char* start) const
    {
        assert(peek() <= start);
        assert(start <= beginWrite());
        // FIXME: replace with memmem()?
        const char* crlf = std::search(start, beginWrite(), kCRLF, kCRLF+2);
        return crlf == beginWrite() ? NULL : crlf;
    }

    const char* findEOL() const
    {
        const void* eol = memchr(peek(), '\n', readableBytes());
        return static_cast<const char*>(eol);
    }

    const char* findEOL(const char* start) const
    {
        assert(peek() <= start);
        assert(start <= beginWrite());
        const void* eol = memchr(start, '\n', beginWrite() - start);
        return static_cast<const char*>(eol);
    }

    void retrieve(size_t len)
    {
        assert(len <= readableBytes());
        if (len < readableBytes())
        {
            m_rIndex += len;
        }
        else
        {
            retrieveAll();
        }
    }

    void retrieveUntil(const char* end)
    {
        assert(peek() <= end);
        assert(end <= beginWrite());
        retrieve(end - peek());
    }

    void retrieveInt64()
    {
        retrieve(sizeof(int64_t));
    }

    void retrieveInt32()
    {
        retrieve(sizeof(int32_t));
    }

    void retrieveInt16()
    {
        retrieve(sizeof(int16_t));
    }

    void retrieveInt8()
    {
        retrieve(sizeof(int8_t));
    }

    void retrieveAll()
    {
        m_rIndex = 0;
        m_wIndex = 0;
    }

    string retrieveAllAsString()
    {
        return retrieveAsString(readableBytes());;
    }

    string retrieveAsString(size_t len)
    {
        assert(len <= readableBytes());
        string result(peek(), len);
        retrieve(len);
        return result;
    }
/*
    StringPiece toStringPiece() const
    {
        return StringPiece(peek(), static_cast<int>(readableBytes()));
    }

    void append(const StringPiece& str)
    {
        append(str.data(), str.size());
    }
*/
    void append(const char* data, size_t len)
    {
        ensureWritableBytes(len);
        std::copy(data, data+len, beginWrite());
        hasWritten(len);
    }

    void append(const void* data, size_t len)
    {
        append(static_cast<const char*>(data), len);
    }

    void ensureWritableBytes(size_t len)
    {
        if (writableBytes() < len)
        {
            makeSpace(len);
        }
        assert(writableBytes() >= len);
    }

    char* beginWrite()
    {
        return m_buf + m_wIndex;
    }

    const char* beginWrite() const
    {
        return m_buf + m_wIndex;
    }

    void hasWritten(size_t len)
    {
        assert(len <= writableBytes());
        m_wIndex += len;
    }

    void unwrite(size_t len)
    {
        assert(len <= readableBytes());
        m_wIndex -= len;
    }

    ///
    /// Append int64_t using network endian
    ///
    void appendInt64(int64_t x)
    {
        int64_t be64 = hostToNetwork64(x);
        append(&be64, sizeof be64);
    }

    ///
    /// Append int32_t using network endian
    ///
    void appendInt32(int32_t x)
    {
        int32_t be32 = hostToNetwork32(x);
        append(&be32, sizeof be32);
    }

    void appendInt16(int16_t x)
    {
        int16_t be16 = hostToNetwork16(x);
        append(&be16, sizeof be16);
    }

    void appendInt8(int8_t x)
    {
        append(&x, sizeof x);
    }

    ///
    /// Read int64_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
    int64_t readInt64()
    {
        int64_t result = peekInt64();
        retrieveInt64();
        return result;
    }

    ///
    /// Read int32_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
    int32_t readInt32()
    {
        int32_t result = peekInt32();
        retrieveInt32();
        return result;
    }

    int16_t readInt16()
    {
        int16_t result = peekInt16();
        retrieveInt16();
        return result;
    }

    int8_t readInt8()
    {
        int8_t result = peekInt8();
        retrieveInt8();
        return result;
    }

    ///
    /// Peek int64_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int64_t)
    int64_t peekInt64() const
    {
        assert(readableBytes() >= sizeof(int64_t));
        int64_t be64 = 0;
        ::memcpy(&be64, peek(), sizeof be64);
        return networkToHost64(be64);
    }

    ///
    /// Peek int32_t from network endian
    ///
    /// Require: buf->readableBytes() >= sizeof(int32_t)
    int32_t peekInt32() const
    {
        assert(readableBytes() >= sizeof(int32_t));
        int32_t be32 = 0;
        ::memcpy(&be32, peek(), sizeof be32);
        return networkToHost32(be32);
    }

    int16_t peekInt16() const
    {
        assert(readableBytes() >= sizeof(int16_t));
        int16_t be16 = 0;
        ::memcpy(&be16, peek(), sizeof be16);
        return networkToHost16(be16);
    }

    int8_t peekInt8() const
    {
        assert(readableBytes() >= sizeof(int8_t));
        int8_t x = *peek();
        return x;
    }

    ///
    /// Prepend int64_t using network endian
    ///
    void prependInt64(int64_t x)
    {
        int64_t be64 = hostToNetwork64(x);
        prepend(&be64, sizeof be64);
    }

    ///
    /// Prepend int32_t using network endian
    ///
    void prependInt32(int32_t x)
    {
        int32_t be32 = hostToNetwork32(x);
        prepend(&be32, sizeof be32);
    }

    void prependInt16(int16_t x)
    {
        int16_t be16 = hostToNetwork16(x);
        prepend(&be16, sizeof be16);
    }

    void prependInt8(int8_t x)
    {
        prepend(&x, sizeof x);
    }

    void prepend(const void* /*restrict*/ data, size_t len)
    {
        assert(len <= frontBytes());
        m_rIndex -= len;
        const char* d = static_cast<const char*>(data);
        std::copy(d, d+len, m_buf+m_rIndex);
    }
/*
    void shrink(size_t reserve)
    {
        // FIXME: use vector::shrink_to_fit() in C++ 11 if possible.
        Buffer other;
        other.ensureWritableBytes(readableBytes()+reserve);
        other.append(toStringPiece());
        swap(other);
    }
*/
    size_t capacity() const
    {
        return m_size;
    }

    /// Read data directly into buffer.
    ///
    /// It may implement with readv(2)
    /// @return result of read(2), @c errno is saved
    ssize_t readNetData(int fd, int* err);

private:

    char* begin()
    {
        return m_buf;
    }

    const char* begin() const
    {
        return m_buf;
    }

    void makeSpace(size_t len)
    {
        if (writableBytes() + frontBytes() < len)
        {
            // FIXME: move readable data
			char *tmp=m_buf;
			m_buf=static_cast<char *>(realloc(m_buf,m_wIndex+len));
			if (m_buf==nullptr)
			{
				printf("realloc error\n");
				m_buf=tmp;
			}
			else
			{
				m_size=m_wIndex+len;
				m_wIndex=readableBytes();
				m_rIndex=0;
			}
        }
        else
        {
            // move readable data to the front, make space inside buffer
			size_t readable = readableBytes();
			memmove(m_buf,m_buf+m_rIndex,readable);
            m_wIndex = readable;
			m_rIndex = 0;
            assert(readable == readableBytes());
        }
    }

private:
    char *m_buf;
	size_t m_size;
    size_t m_rIndex;;
    size_t m_wIndex;

    static const char kCRLF[];
};

}


#endif  //
